<?php

namespace app\user\logic;

use app\Api;

/**
 * 用户支付宝类
 * Class UserAliPay
 * @package app\index\logic
 */
class UserAliPay extends Api
{
    private static $obj = null;

    private $app_id;

    private $private_key_path;

    public function _initialize()
    {
        parent::_initialize();
        $this->app_id = config('alipay.alipay_app_id');
        $this->private_key_path = config('alipay.alipay_private_key_path');
    }


    /**
     * 类入口
     * @return UserAliPay|null
     */
    public static function getInstance()
    {
        if (is_null(self::$obj))
            self::$obj = new self();
        return self::$obj;
    }

    /**
     * 支付宝APP支付
     * @param $pay_sn string 商品单号
     * @param $order_amount float 订单金额
     * @param $subject string 订单信息
     * @param $notify_url string 回调地址
     * @param array $passback_params 附加参数
     * @return string
     */
    public function aliAppPay($pay_sn, $order_amount, $subject, $notify_url, $passback_params = [])
    {
        //支付宝分配给开发者的应用ID
        $mygoods['app_id'] = $this->app_id;
        //接口名称
        $mygoods['method'] = 'alipay.trade.app.pay';
        //请求使用的编码格式，如utf-8,gbk,gb2312等
        $mygoods['charset'] = 'utf-8';
        //商户生成签名字符串所使用的签名算法类型
        $mygoods['sign_type'] = 'RSA';
        //发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
        $mygoods['timestamp'] = date('Y-m-d H:i:s', $this->_now);
        //调用的接口版本，固定为：1.0
        $mygoods['version'] = '1.0';
        //支付宝服务器主动通知商户服务器里指定的页面http/https路径
        $mygoods['notify_url'] = $notify_url;
        //请求参数的集合，最大长度不限，除公共参数外所有请求参数都必须放在这个参数中传递，具体参照各产品快速接入文档

        //商户订单号,64个字符以内、可包含字母、数字、下划线；需保证在商户端不重复
        $mygoods['biz_content']['out_trade_no'] = $pay_sn;
        //订单总金额，单位为元，精确到小数点后两位，取值范围[0.01,100000000] 如果同时传入了【打折金额】，【不可打折金额】，【订单总金额】三者，则必须满足如下条件：【订单总金额】=【打折金额】+【不可打折金额】
        $mygoods['biz_content']['total_amount'] = $order_amount;

        //主题
        $mygoods['biz_content']['subject'] = $subject;
        //销售产品码，商家和支付宝签约的产品码，为固定值QUICK_MSECURITY_PAY
        $mygoods['biz_content']['product_code'] = 'QUICK_MSECURITY_PAY';

        // 公共回传参数，如果请求时传递了该参数，则返回给商户时会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
        $mygoods['biz_content']['passback_params'] = urlencode(json_encode($passback_params));
        //服务器端异步响应地址
        $mygoods['biz_content'] = json_encode($mygoods['biz_content'], JSON_UNESCAPED_UNICODE);

        $mystr = $this->getSignContent($mygoods);
        $mystr2 = $this->getSignContent($mygoods, 1);

        //签名
        $sign = $this->alipaySign($mystr);

        //生成最终签名信息
        $orderInfo = $mystr2 . '&sign=' . urlencode($sign);
        return $orderInfo;
    }

    /**
     * 支付宝退款
     * @param $out_trade_no string 订单号
     * @param $refund_amount float 退款金额
     * @param $refund_reason string 退款原因
     * @param $out_request_no string 退款标识
     * @param $operator_id string 商户的操作员编号
     * @param $store_id string 商户的门店编号
     * @param $terminal_id string 商户的终端编号
     * , $refund_reason = '', $operator_id = 0, $store_id = 0, $terminal_id = 0, $code, $passback_params = []
     */
    public function alipayRefund($out_trade_no, $refund_amount, $out_request_no)
    {
        //支付宝分配给开发者的应用ID
        $mygoods['app_id'] = $this->app_id;
        //接口名称
        $mygoods['method'] = 'alipay.trade.refund';
        //请求使用的编码格式，如utf-8,gbk,gb2312等
        $mygoods['charset'] = 'utf-8';
        //商户生成签名字符串所使用的签名算法类型
        $mygoods['sign_type'] = 'RSA';
        //发送请求的时间，格式"yyyy-MM-dd HH:mm:ss"
        $mygoods['timestamp'] = date('Y-m-d H:i:s', $this->_now);
        //调用的接口版本，固定为：1.0
        $mygoods['version'] = '1.0';
        //商家订单号
        $mygoods['biz_content']['out_trade_no'] = $out_trade_no;
        //退款金额
        $mygoods['biz_content']['refund_amount'] = $refund_amount;
        //退款原因
        //$mygoods['biz_content']['refund_reason'] = $refund_reason;
        //退款唯一标识
        $mygoods['biz_content']['out_request_no'] = $out_request_no;
        //商户的操作员编号
        //$mygoods['biz_content']['operator_id'] = $operator_id;
        //商户的门店编号
        //$mygoods['biz_content']['store_id'] = $store_id;
        //商户的终端编号
        //$mygoods['biz_content']['terminal_id'] = $terminal_id;
        // 公共回传参数，如果请求时传递了该参数，则返回给商户时会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝
        //$mygoods['biz_content']['passback_params'] = urlencode(json_encode(['code' => $code, 'data' => $passback_params]));
        //服务器端异步响应地址
        $mygoods['biz_content'] = json_encode($mygoods['biz_content'], JSON_UNESCAPED_UNICODE);

        $mystr = $this->getSignContent($mygoods);

        //签名
        $sign = $this->alipaySign($mystr);

        $mygoods['sign'] = $sign;

        return $this->_curlPost('https://openapi.alipay.com/gateway.do', $mygoods); //json
    }

    /*************************支付宝支付签名需要使用到的方法*******************************/

    /**
     * 支付宝签名
     * @param string $data 需要签名的字符串
     * @param string $signType
     * @return string
     */
    private function alipaySign($data, $signType = "RSA")
    {
        $priKey = file_get_contents($this->private_key_path);
        $res = openssl_get_privatekey($priKey);

        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');

        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }
        openssl_free_key($res);
        $sign = base64_encode($sign);
        return $sign;
    }

    /**
     * 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param array $params 需要拼接的数组
     * @return string 拼接完成以后的字符串
     */
    public function getSignContent($params, $type = 0)
    {
        ksort($params);

        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, 'UTF-8');
                if ($type == 1) {
                    $v = urlencode($v);
                }
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }

        unset ($k, $v);
        return $stringToBeSigned;
    }

    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    private function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }

    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    private function characet($data, $targetCharset)
    {

        if (!empty($data)) {
            $fileType = 'UTF-8';
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
            }
        }
        return $data;
    }
}
